package com.yykj.common.aspect;

import com.yykj.common.annotation.LocalAddCache;
import com.yykj.common.annotation.LocalRemoveCache;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.GsonUtil;
import com.yykj.common.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class LocalCacheAspect {

    @Pointcut("@annotation(com.yykj.common.annotation.LocalAddCache)")
    public void cache() {
    }

    @Pointcut("@annotation(com.yykj.common.annotation.LocalRemoveCache))")
    public void remove() {
    }

    @Around("cache()")
    public Object doCache(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = signature.getParameterNames();
        LocalAddCache annotation = method.getAnnotation(LocalAddCache.class);
        String key = annotation.key();
        String prefix = annotation.prefix();
        String condition = annotation.condition();
        String expire = annotation.expire();

        EvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }

        Boolean conditionValue = parser.parseExpression(condition).getValue(context, Boolean.class);
        if (Boolean.TRUE.equals(conditionValue)) {//需要缓存
            if (!StringUtils.isEmpty(key)) {
                key = parser.parseExpression(key).getValue(context, String.class);
            } else {
                StringBuilder sb = new StringBuilder();
                for (Object obj : args) {
                    if (null != obj) {
                        sb.append(obj.toString()).append("#");
                    }
                }
                key = MD5Util.md5Encode(sb.toString());
            }
            key = prefix + key;

            Class<?> returnType = method.getReturnType();
            String value = redisService.get(key);
            if (!StringUtils.isEmpty(value)) {
                return GsonUtil.fromJson(value, returnType);
            } else {
                Object proceed = joinPoint.proceed();
                if (null != proceed) {
                    value = GsonUtil.toJson(proceed);
                    redisService.set(key, value);
                    redisService.expire(key,Long.parseLong(expire));
                }
                return proceed;
            }

        } else {//不需要缓存
            return joinPoint.proceed();
        }
    }

    @Before("remove()")
    public void doRemoveCache(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = signature.getParameterNames();
        LocalRemoveCache annotation = method.getAnnotation(LocalRemoveCache.class);
        String key = annotation.key();
        String prefix = annotation.prefix();

        EvaluationContext context = new StandardEvaluationContext();
        ExpressionParser parser = new SpelExpressionParser();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        if (StringUtils.isEmpty(key)) {
            redisService.removeAllKeysLike(prefix);
        } else {
            key = parser.parseExpression(key).getValue(context, String.class);
            redisService.remove(prefix + key);
        }

    }

    private RedisService redisService;

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }
}
